package blockworld;

import grid.items.Body;

import jade.util.leap.Iterator;

import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class World 
{
	private int _width = 0;
	private int _height = 0; 
//	private int _nrAgents = 0;
	private int _shootingDistance = 5;
//	private int _placement = 0;
//	private String _ourTeam = null;
//	private String _owner = null;
	
	//agentId, point
	private Map<Point, String> _bodyMap = null;
	private Map<String,EBody> _encounteredBodies = null;
	//private List<Placement> _bodies = null;
	
	private EBody _myBody;
	private Point _myPos;
	
	
	//private Map<String, Integer> _expectedHPs = null;
	
	public World(int width, int height, int nrAgents, EBody me, Point pos)
	{
		_width = width;
		_height = height;
//		_nrAgents = nrAgents;
//		_ourTeam = ourTeam;
//		_owner = owner;
//		_placement = placement;
		_myBody = me;
		_myPos = pos;
		
		//_bodies = new ArrayList<Placement>();
		_bodyMap = new HashMap<Point,String>();
		_encounteredBodies = new HashMap<String,EBody>();
		
	//	_expectedHPs = new HashMap<String, Integer>();
	}
	
	//called by the manager agent.
	public static List<Placement> placeAgents(int width, int height, int nrAgents, int placement, String ourTeam, String[] agents)
	{
		int x = 0;
		int y = 0;
		int bodyType = 0;
		
		int rowBegin = (width/2) - (((nrAgents/2)-1)/2);
		int columnBegin = (height/2) - (((nrAgents/2)-1)/2);
		
		List<Placement> bodies = new ArrayList<Placement>();
		
		System.out.println("rowBegin" + rowBegin + " columnBegin" + columnBegin + " nrAgents"+ nrAgents);
		System.out.println("width" + width + " height" + height);
		for(int i=0; i<nrAgents && i<agents.length; i++)
		{
			String agent = agents[i];
			bodyType = 2 - (i % 3);
			if(placement == Direction.NORTH)
			{	
				if(i < (nrAgents/2))
				{x = rowBegin + i;	y=height-1;}
				else
				{x = rowBegin + i - nrAgents/2; y=height-2;}
			}
			else if(placement == Direction.EAST)
			{
				if(i < (nrAgents/2))
				{x = width - 1;	y = columnBegin + i;}
				else
				{x =  width - 2; y = columnBegin + i - nrAgents/2; }
			}
			else if(placement == Direction.SOUTH)
			{
				if(i < (nrAgents/2))
				{x = rowBegin + i;	y=0;}
				else
				{x = rowBegin + i - nrAgents/2; y=1;}
			}
			else if(placement == Direction.WEST)
			{
				if(i < (nrAgents/2))
				{x = 0;	y = columnBegin + i;}
				else
				{x =  1; y = columnBegin + i - nrAgents/2; }
			}
			System.out.println("new placement:" + agent + " bodyType" + bodyType);
			bodies.add(new Placement(new EBody(agent,ourTeam, bodyType), x, y));
		}
		return bodies;
	}
	
	public EBody getEBody(String agentId)
	{
		if(_encounteredBodies.containsKey(agentId))
			return _encounteredBodies.get(agentId);
		else
			return null;
	}
	
	public EBody getEBody(Point position)
	{
		String agentId = null;
		
		if(_bodyMap.containsKey(position))
			agentId = _bodyMap.get(position);
		
		return getEBody(agentId);
	}
	
	public void encounteredBody(EBody body)
	{
		if(!_encounteredBodies.containsKey(body.getID()))
		{
			body.setExpectedHP(EBody.STARTING_HP);
			_encounteredBodies.put(body.getID(), body);
		}
		else
		{
			EBody registeredBody = _encounteredBodies.get(body.getID());
			if (registeredBody.getExpectedHP() < 1)
				registeredBody.setExpectedHP(4);
		}
	}
	
	
	public void registerShot(int x, int y, int damage)
	{
		EBody target = getEBody(new Point(x,y));
		if(target != null)
		{
			target.setExpectedHP(target.getExpectedHP()-damage);
		}
	}
	
	
	public void registerShot(int x, int y, int damage, String name, String team, int type )
	{
		EBody target = getEBody(new Point(x,y));
		if(target != null)
		{
			target.setExpectedHP(target.getExpectedHP()-damage);
		}
		else
		{
			this.encounteredBody( new EBody(name, team, type, 10-damage));
		}
	}
	
	public void shoot(int x, int y)
	{
		EBody body = getEBody(new Point(x,y));
		if(body != null)
			body.inflictDamage(_myBody.getType());
	}
	
	
	
	public String toString()
	{
		StringBuilder stringBuilder = new StringBuilder();
		StringBuilder bottomLine = new StringBuilder("   ");
		for(int y=_height-1; y>=0; y--)
		{
			stringBuilder.append(y);
			stringBuilder.append(":|");
			for(int x=0; x<_width; x++)
			{
				EBody ebody = getEBody(new Point(x,y));
				if(ebody != null)
				{
					stringBuilder.append(ebody.toShortString(_myBody.getTeam()));
				}
				else if(_myPos.equals(new Point(x,y)))
				{
					stringBuilder.append(" X ");
				}
				else
				{
					stringBuilder.append("   ");
				}
				stringBuilder.append("|");
				if(y==0)
					bottomLine.append(" " + x + "  ");
			}
			stringBuilder.append("\n");
		}
		stringBuilder.append(bottomLine.toString() + "\n");
		
		return stringBuilder.toString();
	}
	
	
	/**
	 * Process sensed information in own world-representation
	 * @param myPlacement
	 * @param otherAgents
	 */
	public void processSense( Point myPosition, Map<Body, Point> otherAgents )
	{
		_myPos = myPosition;
		
		// turn map into list
		Placement tmpPlacement;
		Map<Point, String> newAgents = new HashMap<Point,String>();
		
		for( Body body : otherAgents.keySet() )
		{
			EBody ebody = new EBody(body);
			
			//if agent sensed is not myself
			if(!ebody.equals(_myBody))
			{
				encounteredBody(ebody);
				newAgents.put(otherAgents.get( body ), ebody.getID());				
			}			
			
			// Is agent a teammember and new in range? Then send sense info
			if( ebody.sameTeam(_myBody) && (!_bodyMap.containsValue(ebody)))
			{
				this.sendTeamInfo(ebody);
			}
			
		}
		
		_bodyMap.clear();
		_bodyMap.putAll(newAgents);
	}
	
	
	private void sendTeamInfo(EBody body) {
		// TODO Auto-generated method stub
		
	}

	/**
	 * Get the list of enemies whitin the shootingrange of this agent.
	 * The list is ordered by the best effect of attacking that enemy.s
	 * @return
	 */
	public List<Placement> getEnemiesInShootingRange()
	{
		List<Placement> enemies = new ArrayList<Placement>();
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			//within shooting range
			if(_myPos.distance(entry.getKey()) <= _shootingDistance)
			{
				EBody enemy = getEBody(entry.getValue());
				if(!enemy.sameTeam(_myBody))
				{
					enemies.add(new Placement(enemy, entry.getKey()));
				}
			}
		}
		
		BestTargetCompPlacement comparator = new BestTargetCompPlacement(_myBody);
		Collections.sort(enemies,comparator);
		
		return enemies;
	}
	
	
	/** 
	 * Get the list of enemies _outside_ the shootingrange of this agent
	 * 
	 * @return
	 */
	public List<Placement> getEnemiesOutShootingRange()
	{
		List<Placement> enemies = new ArrayList<Placement>();
		
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			//outside shooting range
			if(_myPos.distance(entry.getKey()) > _shootingDistance)
			{
				EBody enemy = getEBody(entry.getValue());
				if(!enemy.sameTeam(_myBody))
				{
					enemies.add(new Placement(enemy, entry.getKey()));
				}
			}
		}
		//not sorted
		return enemies;	
	}
	
	
	/**
	 * Get all the enemies in the sense-range, sorted by best effect on attacking
	 * 
	 * @return
	 */
	public List<Placement> getEnemies()
	{
		
		List<Placement> enemies = new ArrayList<Placement>();
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			EBody enemy = getEBody(entry.getValue());
			if(!enemy.sameTeam(_myBody))
			{
				enemies.add(new Placement(enemy, entry.getKey()));
			}
		}
		
		BestTargetCompPlacement comparator = new BestTargetCompPlacement(_myBody);
		Collections.sort(enemies,comparator);
		
		return enemies;
	}
	
	
	/**
	 * Get all the teammembers is the sense range
	 * @return
	 */
	public List<Placement> getTeamMembers()
	{
		List<Placement> team = new ArrayList<Placement>();
		
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			EBody teamMate = getEBody(entry.getValue());
			if(teamMate.sameTeam(_myBody))
			{
				team.add(new Placement(teamMate, entry.getKey()));
			}
		}
		//unsorted
		return team;	
	}
	
	
	/**
	 * Get all the teammembers who can shoot at a partical position
	 * @param toShoot
	 * @return
	 */
	public List<Placement> getTeamForShootPosition( Point toShoot )
	{
		
		List<Placement> team = new ArrayList<Placement>();
		
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			EBody teamMate = getEBody(entry.getValue());
			if(teamMate.sameTeam(_myBody))
			{
				if(entry.getKey().distance(toShoot) <= _shootingDistance)
					team.add(new Placement(teamMate, entry.getKey()));
			}
		}
		//unsorted
		return team;	
	}
	
	
	/**
	 * Get all the teammembers whitin the sense range but outside the shoot range
	 * for a particual position
	 * @param toShoot
	 * @return
	 */
	public List<Placement> getTeamOutShootPosition( Point toShoot )
	{
		List<Placement> team = new ArrayList<Placement>();
		
		for(Entry<Point,String> entry : _bodyMap.entrySet())
		{
			EBody teamMate = getEBody(entry.getValue());
			if(teamMate.sameTeam(_myBody))
			{
				if(entry.getKey().distance(toShoot) > _shootingDistance)
					team.add(new Placement(teamMate, entry.getKey()));
			}
		}
		//unsorted
		return team;		
	}
	
	
	
}
